﻿/*
 * Created by SharpDevelop.
 * Author: Taranu Bogdan
 * Date: 5/4/2012
 * Time: 5:02 PM
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ServerClientCommon;

namespace SensorClient
{
    /// <summary>
    /// It is used to generate SensorValueContainer from byte 
    /// packages.
    /// </summary>
    public class SensorContainerAdapter
    {
        #region Constants
        /// <summary>
        /// The humidity raw value for which is possible a flood
        /// </summary>
        private const int HUMIDITY_DANGER_RAW_LIMIT = 950;
        /// <summary>
        /// The light raw value considered as dim light
        /// </summary>
        private const int LIGHT_DANGER_RAW_LIMIT = 300;
        /// <summary>
        /// The lower limit of a fractionary raw value
        /// </summary>
        private const int FRACTIONARY_LOWER_LIMIT = 0;
        /// <summary>
        /// The upper limit of a fractionary raw value
        /// </summary>
        private const int FRACTIONARY_UPPER_LIMIT = 100;
        #endregion

        #region Members
        /// <summary>
        /// The preset for the non-boolean sensors
        /// </summary>
        private NormalSensorsPreset nPreset;
        /// <summary>
        /// The preset for boolean sensors
        /// </summary>
        private BooleanSensorsPreset bPreset;
        /// <summary>
        /// The id of the module who sends the information
        /// </summary>
        private int moduleId;
        #endregion

        #region Constructors
        /// <summary>
        /// Creates a SensorContainerAdapter object 
        /// </summary>
        /// <param name="nPreset">A preset for the limits of normal sensors</param>
        /// <param name="bPreset">A preset for overwriting boolean sensors</param>
        public SensorContainerAdapter(NormalSensorsPreset nPreset,
                                      BooleanSensorsPreset bPreset,
                                      int moduleId)
        {
            this.nPreset = nPreset; //Shallow copy
            this.bPreset = bPreset; //Shallow copy
            this.moduleId = moduleId;
        }

        #endregion

        #region Public Methods
        /// <summary>
        /// Converts a byte sensor package to a SensorValuesContainer
        /// </summary>
        /// <param name="package">A byte vector who has a format according
        /// to the PackageFormat</param>
        /// <returns>The real values of the sensors packed in SensorsValuesContainer</returns>
        public SensorContainer ConvertRawToReal(byte[] package)
        {
            int index = 0;
            int temp = 0;
            int tempf = 0;

            int[] ecg = null;
            int[] hta = null;
            int pulse;
            float bodyTemp;
            float weight;
            int glicemia;
            int respiratoryFrequency;
            int ambientTemperature;
            bool humidityDanger;
            bool notEnoughLight;
            bool gasProblem;
            bool rfidEmptyRoom;
            bool powerDown;

            //ECG
            ecg = new int[SensorContainer.ECG_SIZE];
            for (index = (int)PackageFormat.SensorPositionStart.ECG;
                index < (int)PackageFormat.SensorPositionStop.ECG;
                index = index + 2)
            {
                temp = GetIntFrom2Bytes(package[index + 1], package[index]);
                ecg[index / 2] = ApplyScale(temp,
                                          (int)PackageFormat.BitMaxValues.TEN_BITS_MAX,
                                           nPreset.EcgLowerLimit, nPreset.EcgUpperLimit);
            }

            //HTA
            //Systolic
            index = (int)PackageFormat.SensorPositionStart.HTA;
            hta = new int[SensorContainer.HTA_SIZE];
            temp = GetIntFrom2Bytes(package[index + 1], package[index]);
            hta[SensorContainer.HTA_SYSTOLIC]
                = ApplyScale(temp, (int)PackageFormat.BitMaxValues.TEN_BITS_MAX,
                             nPreset.HtaLowerLimit, nPreset.HtaUpperLimit);
            //Diastolic
            index = index + 2;
            temp = GetIntFrom2Bytes(package[index + 1], package[index]);
            hta[SensorContainer.HTA_DIASTOLIC]
                = ApplyScale(temp,
                             (int)PackageFormat.BitMaxValues.TEN_BITS_MAX,
                             nPreset.HtaLowerLimit, nPreset.HtaUpperLimit);

            //Pulse
            index = (int)PackageFormat.SensorPositionStart.PULSE;
            temp = GetIntFrom2Bytes(package[index + 1], package[index]);
            pulse = ApplyScale(temp,
                              (int)PackageFormat.BitMaxValues.TEN_BITS_MAX,
                               nPreset.PulseLowerLimit, nPreset.PulseUpperLimit);

            //Body temperature
            index = (int)PackageFormat.SensorPositionStart.BODY_TEMPERATURE;
            temp = ApplyScale(package[index],
                              (int)PackageFormat.BitMaxValues.EIGHT_BITS_MAX,
                               nPreset.BodyTemperatureLowerLimit,
                               nPreset.BodyTemperatureUpperLimit);
            tempf = ApplyScale(package[index + 1],
                               (int)PackageFormat.BitMaxValues.FOUR_BITS_MAX,
                                FRACTIONARY_LOWER_LIMIT, FRACTIONARY_UPPER_LIMIT);
            bodyTemp = GetFloatFromFixedPoint(temp, tempf);

            //Weight
            index = (int)PackageFormat.SensorPositionStart.WEIGHT;
            temp = GetIntFrom2Bytes(package[index + 1], package[index]);
            tempf = (int)(package[index + 2]);
            temp = ApplyScale(temp,
                             (int)PackageFormat.BitMaxValues.TEN_BITS_MAX,
                              nPreset.WeightLowerLimit, nPreset.WeightUpperLimit);
            tempf = ApplyScale(package[index + 1],
                               (int)PackageFormat.BitMaxValues.FOUR_BITS_MAX,
                               FRACTIONARY_LOWER_LIMIT, FRACTIONARY_UPPER_LIMIT);
            weight = GetFloatFromFixedPoint(temp, tempf);

            //Glicemia
            index = (int)PackageFormat.SensorPositionStart.GLICEMY;
            temp = GetIntFrom2Bytes(package[index + 1], package[index]);
            glicemia = ApplyScale(temp,
                                 (int)PackageFormat.BitMaxValues.TEN_BITS_MAX,
                                  nPreset.GlicemiaLowerLimit, nPreset.GlicemiaUpperLimit);

            //Respiratory frequency
            index = (int)PackageFormat.SensorPositionStart.RESPIRATORY_FREQUENCY;
            temp = package[index];
            respiratoryFrequency = ApplyScale(temp,
                                             (int)PackageFormat.BitMaxValues.EIGHT_BITS_MAX,
                                              nPreset.RespiratoryFrequencyLowerLimit,
                                              nPreset.RespiratoryFrequenctUpperLimit);

            //Ambient temperature
            index = (int)PackageFormat.SensorPositionStart.AMBIENT_TEMPERATURE;
            temp = GetIntFrom2Bytes(package[index + 1], package[index]);
            ambientTemperature = ApplyScale(temp,
                                            (int)PackageFormat.BitMaxValues.TEN_BITS_MAX,
                                             nPreset.AmbientTemperatureLowerLimit,
                                             nPreset.AmbientTemperatureUpperLimit);
            if (bPreset.UsePreset == true)
            {
                humidityDanger = bPreset.HumidityDanger;
                notEnoughLight = bPreset.NotEnoughLight;
                gasProblem = bPreset.GasProblem;
                rfidEmptyRoom = bPreset.RfidEmptyRoom;
                powerDown = bPreset.PowerDown;
            }
            else
            {
                //Humidity sensor
                index = (int)PackageFormat.SensorPositionStart.HUMIDITY;
                temp = GetIntFrom2Bytes(package[index + 1], package[index]);
                if (temp > HUMIDITY_DANGER_RAW_LIMIT)
                {
                    humidityDanger = true;
                }
                else
                {
                    humidityDanger = false;
                }

                //Light sensor
                index = (int)PackageFormat.SensorPositionStart.LIGHT;
                temp = GetIntFrom2Bytes(package[index + 1], package[index]);
                if (temp < LIGHT_DANGER_RAW_LIMIT)
                {
                    notEnoughLight = true;
                }
                else
                {
                    notEnoughLight = false;
                }

                //Gas, RFID, Voltage
                index = (int)PackageFormat.SensorPositionStart.RFID_VOLTAGE_GAS;
                temp = (int)package[index];
                //Gas
                if ((temp & (int)(1 << ((int)PackageFormat.GAS_SHIFT))) != 0)
                {
                    gasProblem = true;
                }
                else
                {
                    gasProblem = false;
                }
                //RFID
                if ((temp & (int)(1 << ((int)PackageFormat.RFID_SHIFT))) != 0)
                {
                    rfidEmptyRoom = true;
                }
                else
                {
                    rfidEmptyRoom = false;
                }
                //Voltage
                if ((temp & (int)(1 << ((int)PackageFormat.VOLTAGE_SHIFT))) != 0)
                {
                    powerDown = true;
                }
                else
                {
                    powerDown = false;
                }
            }
            return new SensorContainer(moduleId,
                                       ecg,
                                       hta,
                                       pulse,
                                       bodyTemp,
                                       weight,
                                       glicemia,
                                       respiratoryFrequency,
                                       ambientTemperature,
                                       notEnoughLight,
                                       humidityDanger,
                                       rfidEmptyRoom,
                                       powerDown,
                                       gasProblem);
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Concatenates 2 bytes
        /// </summary>
        /// <param name="msb">Most significant byte</param>
        /// <param name="lsb">Least significant byte</param>
        /// <returns>The result of the concatenation</returns>
        private int GetIntFrom2Bytes(byte msb, byte lsb)
        {
            return (int)(((int)msb << 8) | (int)(lsb));
        }
        /// <summary>
        /// Creates a floating point variable from two
        /// integers represeting a fixed point number.
        /// </summary>
        /// <param name="integer">The integer part of the 
        /// fixed point variable</param>
        /// <param name="fractionary">The fractionary part of the 
        /// fixed point variable</param>
        /// <returns>The result of the conversion</returns>
        private float GetFloatFromFixedPoint(int integer,
                                             int fractionary)
        {
            return (float)(integer) +
                   (float)((float)fractionary / (float)FRACTIONARY_UPPER_LIMIT);
        }
        /// <summary>
        /// Creates a floating point variable from 3 bytes
        /// integers represeting a fixed point number.
        /// </summary>
        /// <param name="iMsb">The most significant byte of the 
        /// integer part</param>
        /// <param name="iLsb">The least significant byte of the
        /// integer part</param>
        /// <param name="fractionary">The byte containing the
        /// fractionary part</param>
        /// <returns></returns>
        private float GetFloatFromFixedPoint(byte iMsb, byte iLsb,
                                             byte fractionary)
        {
            return (float)(GetIntFrom2Bytes(iMsb, iLsb) +
                   (float)((float)fractionary / (float)FRACTIONARY_UPPER_LIMIT));
        }
        /// <summary>
        /// Scales a integer value
        /// </summary>
        /// <param name="unscaledValue">The unscaled value</param>
        /// <param name="unscaledMax">The maximum value that the
        /// unscaled value can take</param>
        /// <param name="scaledMin">The miniumum value of the scale</param>
        /// <param name="scaledMax">The maximum value of the scale</param>
        /// <returns></returns>
        private int ApplyScale(int unscaledValue, int unscaledMax,
                               int scaledMin, int scaledMax)
        {
            return ((unscaledValue * (scaledMax - scaledMin)) / unscaledMax) + scaledMin;
        }
        #endregion
    }
}
